#include "animationpathcontrol.h"
#include <math.h>

namespace CoreBase
{
	AnimationPathCallbackExt::AnimationPathCallbackExt(const osg::AnimationPathCallback& apc,osg::Node* withnode):
	osg::AnimationPathCallback(apc),_lastStopTime(-1),_withNode(withnode)
	{
		initAnimation();
	}

	AnimationPathCallbackExt::~AnimationPathCallbackExt(void)
	{}

	void AnimationPathCallbackExt::operator()(osg::Node* node, osg::NodeVisitor* nv)
	{
		if (_animationPath.valid() && _aniamtionPeriod.isValid() &&
			nv->getVisitorType() == osg::NodeVisitor::UPDATE_VISITOR && nv->getFrameStamp())
		{
			double time = nv->getFrameStamp()->getSimulationTime();
			_latestTime = time;
			if (_startAnimationApply._applyWait)
			{
				if (_startAnimationApply._setStartTime)
				{
					_firstTime = time - _startAnimationApply._startTime + _aniamtionPeriod.getPeriodStartTime();
				}
				else
				{
					_firstTime = time;
				}
				update(*node);
				_startAnimationApply._applyWait = false;
				_startAnimationApply._setStartTime = false;
				_pause = false;
			}
			else if (_stopAnimationApply._applyWait)
			{
				if (_firstTime==DBL_MAX) _firstTime = time;
				if (_stopAnimationApply._setStopTime)
				{
					_latestTime = _firstTime + _stopAnimationApply._stopTime - _aniamtionPeriod.getPeriodStartTime();
				}
				update(*node);
				_lastStopTime = getAnimationTime();
				_stopAnimationApply._applyWait = false;
				_stopAnimationApply._setStopTime = false;
				_pause = true;
			}		
			else if (!_pause)
			{
				// Only update _firstTime the first time, when its value is still DBL_MAX
				if (_firstTime==DBL_MAX) _firstTime = time;
				update(*node);
			}
			updatePause();
		}
		// must call any nested node callbacks and continue subgraph traversal.
		NodeCallback::traverse(node,nv);
	}

	double AnimationPathCallbackExt::getAnimationTime() const
	{
		double animationtime = ((_latestTime-_firstTime)-_timeOffset)*_timeMultiplier;
		if (_animationPath->getLoopMode() == osg::AnimationPath::NO_LOOPING)
		{
			if (animationtime>abs(_aniamtionPeriod.getPeriodTime()))
			{
				animationtime = _aniamtionPeriod.getPeriodEndTime();			
			}
			else
			{
				if(_aniamtionPeriod.getPeriodTime()>0)
					animationtime  += _aniamtionPeriod.getPeriodStartTime();
				else
					animationtime = _aniamtionPeriod.getPeriodStartTime() - animationtime;
			}
		}
		else
		{
			int nLoopTime = floor(animationtime/abs(_aniamtionPeriod.getPeriodTime()));
			animationtime = fmod(animationtime,abs(_aniamtionPeriod.getPeriodTime()));
			if (_startAnimationApply._setLoopTime)
			{
				if (nLoopTime>=_startAnimationApply._loopTime)
				{
					animationtime = _aniamtionPeriod.getPeriodEndTime();				
				}
				else
				{
					if(_aniamtionPeriod.getPeriodTime()>0)
						animationtime  += _aniamtionPeriod.getPeriodStartTime();
					else
						animationtime = _aniamtionPeriod.getPeriodStartTime() - animationtime;
				}
			}
		}
		return animationtime;
	}

	void AnimationPathCallbackExt::updatePause()
	{
		double animationtime = ((_latestTime-_firstTime)-_timeOffset)*_timeMultiplier;
		if (_animationPath->getLoopMode() == osg::AnimationPath::NO_LOOPING)
		{
			if (animationtime>abs(_aniamtionPeriod.getPeriodTime()))
			{
				_pause = true;
			}
		}
		else
		{
			int nLoopTime = floor(animationtime/abs(_aniamtionPeriod.getPeriodTime()));
			if (_startAnimationApply._setLoopTime)
			{
				if (nLoopTime>=_startAnimationApply._loopTime)
				{
					_startAnimationApply._setLoopTime = false;
					_pause = true;
				}
			}
		}	
	}

	void AnimationPathCallbackExt::startAnimationFromBegin()
	{
		if (_aniamtionPeriod.isValid())
		{
			startAnimation(_aniamtionPeriod.getPeriodStartTime());
		}
	}

	void AnimationPathCallbackExt::startAnimation()
	{
		_startAnimationApply._applyWait = true;
		_startAnimationApply._setStartTime = false;
		_stopAnimationApply._applyWait = false;
	}

	void AnimationPathCallbackExt::startAnimation(double starttime)
	{
		_startAnimationApply._applyWait = true;
		_startAnimationApply._setStartTime = true;
		if (_aniamtionPeriod.isValid())
		{
			if(starttime>_aniamtionPeriod.getPeriodStartTime())
			{
				_startAnimationApply._startTime = starttime;
			}
			else
			{
				_startAnimationApply._startTime = _aniamtionPeriod.getPeriodStartTime();
			}
		}	
		_stopAnimationApply._applyWait = false;
	}

	void AnimationPathCallbackExt::stopAnimationAtEnd()
	{
		if(_aniamtionPeriod.isValid())
		{
			stopAnimation(_aniamtionPeriod.getPeriodEndTime());
		}
	}

	void AnimationPathCallbackExt::stopAnimation()
	{
		_stopAnimationApply._applyWait = true;
		_stopAnimationApply._setStopTime = false;
		_startAnimationApply._applyWait = false;
	}

	void AnimationPathCallbackExt::stopAnimation(double stoptime)
	{
		_stopAnimationApply._applyWait = true;
		_stopAnimationApply._setStopTime = true;
		_stopAnimationApply._stopTime = stoptime;
		if(_aniamtionPeriod.isValid())
		{
			if (stoptime<=_aniamtionPeriod.getPeriodStartTime())
			{
				_stopAnimationApply._stopTime = _aniamtionPeriod.getPeriodStartTime()+0.0001;
			}
			else if (stoptime<_aniamtionPeriod.getPeriodEndTime())
			{
				_stopAnimationApply._stopTime = stoptime;
			}
			else
			{
				_stopAnimationApply._stopTime = _aniamtionPeriod.getPeriodEndTime()-0.0001;
			}
		}
		_startAnimationApply._applyWait = false;
	}

	void AnimationPathCallbackExt::setStartTime(double starttime)
	{
		_aniamtionPeriod.setPeriodStartTime(starttime);
	}

	double AnimationPathCallbackExt::setStartTime()
	{
		return _aniamtionPeriod.getPeriodStartTime();	
	}

	void AnimationPathCallbackExt::setStopTime(double stoptime)
	{
		_aniamtionPeriod.setPeriodEndTime(stoptime);
	}

	double AnimationPathCallbackExt::getStopTime()
	{
		return _aniamtionPeriod.getPeriodEndTime();	
	}

	double AnimationPathCallbackExt::getLastStopTime()
	{
		return _lastStopTime;
	}

	void AnimationPathCallbackExt::setAnimationMode(osg::AnimationPath::LoopMode animationmode, int nLoopTime)
	{
		if (_animationPath.valid())
		{
			_animationPath->setLoopMode(animationmode);
		}
		_startAnimationApply._loopMode = animationmode;
		if (nLoopTime>0)
		{
			_startAnimationApply._setLoopTime = true;
			_startAnimationApply._loopTime = nLoopTime;
		}
		else
		{
			_startAnimationApply._setStartTime = false;
		}
	}

	void AnimationPathCallbackExt::initAnimation()
	{
		if (_animationPath.valid())
		{
			setStartTime(_animationPath->getFirstTime());
			setStopTime(_animationPath->getLastTime());
		}
		setAnimationMode(osg::AnimationPath::NO_LOOPING);
		_pause = true;
	}

	AnimationPathCallbackControl::AnimationPathCallbackControl()
	{}

	AnimationPathCallbackControl::~AnimationPathCallbackControl()
	{}

	void AnimationPathCallbackControl::addAnimationPathCallback(osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback)
	{
		_listAnimationPathCallback.push_back(animationPathCallback);
	}

	void AnimationPathCallbackControl::startAnimationFromBegin()
	{
		osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback;
		foreach(animationPathCallback,_listAnimationPathCallback)
		{
			animationPathCallback->startAnimationFromBegin();
		}
	}

	void AnimationPathCallbackControl::startAnimation()
	{
		osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback;
		foreach(animationPathCallback,_listAnimationPathCallback)
		{
			animationPathCallback->startAnimation();
		}
	}

	void AnimationPathCallbackControl::startAnimation(double starttime)
	{
		osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback;
		foreach(animationPathCallback,_listAnimationPathCallback)
		{
			animationPathCallback->startAnimation(starttime);
		}
	}

	void AnimationPathCallbackControl::stopAnimationAtEnd()
	{
		osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback;
		foreach(animationPathCallback,_listAnimationPathCallback)
		{
			animationPathCallback->stopAnimationAtEnd();
		}
	}

	void AnimationPathCallbackControl::stopAnimation()
	{
		osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback;
		foreach(animationPathCallback,_listAnimationPathCallback)
		{
			animationPathCallback->stopAnimation();
		}
	}

	void AnimationPathCallbackControl::stopAnimation(double stoptime)
	{
		osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback;
		foreach(animationPathCallback,_listAnimationPathCallback)
		{
			animationPathCallback->stopAnimation(stoptime);
		}
	}

	void AnimationPathCallbackControl::setAnimationMode(osg::AnimationPath::LoopMode animationmode, int nLoopTime)
	{
		osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback;
		foreach(animationPathCallback,_listAnimationPathCallback)
		{
			animationPathCallback->setAnimationMode(animationmode,nLoopTime);
		}
	}

	void AnimationPathCallbackControl::setStartTime(double starttime)
	{
		osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback;
		foreach(animationPathCallback,_listAnimationPathCallback)
		{
			animationPathCallback->setStartTime(starttime);
		}
	}

	double AnimationPathCallbackControl::setStartTime()
	{
		if(_listAnimationPathCallback.count()>0)
		{
			return _listAnimationPathCallback.first()->setStartTime();
		}
		return -1;
	}

	void AnimationPathCallbackControl::setStopTime(double stoptime)
	{
		osg::ref_ptr<AnimationPathCallbackExt> animationPathCallback;
		foreach(animationPathCallback,_listAnimationPathCallback)
		{
			animationPathCallback->setStopTime(stoptime);
		}
	}

	double AnimationPathCallbackControl::getStopTime()
	{
		if(_listAnimationPathCallback.count()>0)
		{
			return _listAnimationPathCallback.first()->getStopTime();
		}
		return -1;
	}

	double AnimationPathCallbackControl::getLastStopTime()
	{
		if(_listAnimationPathCallback.count()>0)
		{
			return _listAnimationPathCallback.first()->getLastStopTime();
		}
		return -1;
	}

	bool AnimationPathCallbackControl::haveAnimationPathCallback(osg::Node* node)
	{
		if(!node->asGroup())
		{
			osg::AnimationPathCallback* animationcallback = dynamic_cast<osg::AnimationPathCallback*>(node->getUpdateCallback());
			if(animationcallback)
			{
				return true;
			}		
			return false;
		}
		QList<osg::Group*> listGroup;
		osg::Group* pgroup = node->asGroup();
		if(pgroup)
		{
			listGroup.push_back(pgroup);
			osg::AnimationPathCallback* animationcallback = dynamic_cast<osg::AnimationPathCallback*>(pgroup->getUpdateCallback());
			if(animationcallback)
			{
				return true;
			}
		}
		while(listGroup.size()>0)
		{
			osg::Group* pfrontgroup = listGroup.front();
			listGroup.pop_front();
			int nNode = pfrontgroup->getNumChildren();
			for(int i=0; i<nNode; i++)
			{
				node = pfrontgroup->getChild(i);
				if(!node->asGroup())
				{
					osg::AnimationPathCallback* animationcallback = dynamic_cast<osg::AnimationPathCallback*>(node->getUpdateCallback());
					if(animationcallback)
					{
						return true;
					}				
				}
				else
				{
					osg::Group* pgroup = node->asGroup();
					if(pgroup)
					{
						listGroup.push_back(pgroup);
						osg::AnimationPathCallback* animationcallback = dynamic_cast<osg::AnimationPathCallback*>(pgroup->getUpdateCallback());
						if(animationcallback)
						{
							return true;
						}
					}
				}
			}
		}
		return false;
	}

	void AnimationPathCallbackControl::setupNode(osg::Node* node)
	{
		if(!node->asGroup())
		{
			osg::AnimationPathCallback* animationcallback = dynamic_cast<osg::AnimationPathCallback*>(node->getUpdateCallback());
			if(animationcallback)
			{
				osg::ref_ptr<AnimationPathCallbackExt> animationcallbackext = new AnimationPathCallbackExt(*animationcallback,node);
				node->setUpdateCallback(animationcallbackext.get());
				addAnimationPathCallback(animationcallbackext);
			}		
			return;
		}
		QList<osg::Group*> listGroup;
		osg::Group* pGroup = node->asGroup();
		if(pGroup)
		{
			listGroup.push_back(pGroup);
			osg::AnimationPathCallback* animationcallback = dynamic_cast<osg::AnimationPathCallback*>(pGroup->getUpdateCallback());
			if(animationcallback)
			{
				osg::ref_ptr<AnimationPathCallbackExt> animationcallbackext = new AnimationPathCallbackExt(*animationcallback,pGroup);
				pGroup->setUpdateCallback(animationcallbackext.get());
				addAnimationPathCallback(animationcallbackext);
			}
		}
		while(listGroup.size()>0)
		{
			osg::Group* pFrontGroup = listGroup.front();
			listGroup.pop_front();
			int nNode = pFrontGroup->getNumChildren();
			for(int i=0; i<nNode; i++)
			{
				node = pFrontGroup->getChild(i);
				if(!node->asGroup())
				{
					osg::AnimationPathCallback* animationcallback = dynamic_cast<osg::AnimationPathCallback*>(node->getUpdateCallback());
					if(animationcallback)
					{
						osg::ref_ptr<AnimationPathCallbackExt> animationcallbackext = new AnimationPathCallbackExt(*animationcallback,node);
						node->setUpdateCallback(animationcallbackext.get());
						addAnimationPathCallback(animationcallbackext);
					}				
				}
				else
				{
					osg::Group* pGroup = node->asGroup();
					if(pGroup)
					{
						listGroup.push_back(pGroup);
						osg::AnimationPathCallback* animationcallback = dynamic_cast<osg::AnimationPathCallback*>(pGroup->getUpdateCallback());
						if(animationcallback)
						{
							osg::ref_ptr<AnimationPathCallbackExt> animationcallbackext = new AnimationPathCallbackExt(*animationcallback,pGroup);
							pGroup->setUpdateCallback(animationcallbackext.get());
							addAnimationPathCallback(animationcallbackext);
						}
					}
				}			
			}
		}
	}
}